 /*******************************************************************************
  * Copyright (c) 2003, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.navigator;

 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.Collections ;
 import java.util.HashMap ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.Map ;
 import java.util.Set ;
 import java.util.SortedSet ;
 import java.util.TreeSet ;
 import java.util.WeakHashMap ;

 import org.eclipse.core.runtime.ISafeRunnable;
 import org.eclipse.core.runtime.SafeRunner;
 import org.eclipse.jface.viewers.ILabelProvider;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.StructuredViewer;
 import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.IMemento;
 import org.eclipse.ui.internal.navigator.dnd.NavigatorDnDService;
 import org.eclipse.ui.internal.navigator.extensions.ExtensionPriorityComparator;
 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptor;
 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentDescriptorManager;
 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension;
 import org.eclipse.ui.internal.navigator.extensions.NavigatorViewerDescriptor;
 import org.eclipse.ui.internal.navigator.extensions.NavigatorViewerDescriptorManager;
 import org.eclipse.ui.internal.navigator.extensions.StructuredViewerManager;
 import org.eclipse.ui.internal.navigator.sorters.NavigatorSorterService;
 import org.eclipse.ui.navigator.IDescriptionProvider;
 import org.eclipse.ui.navigator.IExtensionActivationListener;
 import org.eclipse.ui.navigator.IExtensionStateModel;
 import org.eclipse.ui.navigator.IMementoAware;
 import org.eclipse.ui.navigator.INavigatorActivationService;
 import org.eclipse.ui.navigator.INavigatorContentDescriptor;
 import org.eclipse.ui.navigator.INavigatorContentExtension;
 import org.eclipse.ui.navigator.INavigatorContentService;
 import org.eclipse.ui.navigator.INavigatorContentServiceListener;
 import org.eclipse.ui.navigator.INavigatorDnDService;
 import org.eclipse.ui.navigator.INavigatorFilterService;
 import org.eclipse.ui.navigator.INavigatorPipelineService;
 import org.eclipse.ui.navigator.INavigatorSaveablesService;
 import org.eclipse.ui.navigator.INavigatorSorterService;
 import org.eclipse.ui.navigator.INavigatorViewerDescriptor;

 /**
  * <p>
  * Provides centralized access to the information provided by
  * NavigatorContentExtensions. Can be instantiated as needed, but should be
  * cached for active viewers. Information specific to a given viewer will be
  * cached by the NavigatorContentService, not including ContentProviders and
  * Label Providers created by {@link #createCommonContentProvider()}and
  * {@link #createCommonLabelProvider()}respectively.
  * </p>
  *
  * <p>
  * The following class is experimental until fully documented.
  * </p>
  */
 public class NavigatorContentService implements IExtensionActivationListener,
         IMementoAware, INavigatorContentService {

     private static final NavigatorContentDescriptorManager CONTENT_DESCRIPTOR_REGISTRY = NavigatorContentDescriptorManager
             .getInstance();

     private static final NavigatorViewerDescriptorManager VIEWER_DESCRIPTOR_REGISTRY = NavigatorViewerDescriptorManager
             .getInstance();

     private static final ITreeContentProvider[] NO_CONTENT_PROVIDERS = new ITreeContentProvider[0];

     private static final ILabelProvider[] NO_LABEL_PROVIDERS = new ILabelProvider[0];

     private static final INavigatorContentDescriptor[] NO_DESCRIPTORS = new INavigatorContentDescriptor[0];

     private static final String [] NO_EXTENSION_IDS = new String [0];

     private final NavigatorViewerDescriptor viewerDescriptor;

     private final List listeners = new ArrayList ();

     /*
      * A map of (String-based-Navigator-Content-Extension-IDs,
      * NavigatorContentExtension-objects)-pairs
      */
     private final Map contentExtensions = new HashMap ();

     private StructuredViewerManager structuredViewerManager;

     private ITreeContentProvider[] rootContentProviders;

     private WeakHashMap contributionMemory;

     private ITreeContentProvider contentProvider;

     private ILabelProvider labelProvider;

     private final VisibilityAssistant assistant;

     private NavigatorFilterService navigatorFilterService;

     private INavigatorSorterService navigatorSorterService;

     private INavigatorPipelineService navigatorPipelineService;

     private INavigatorDnDService navigatorDnDService;

     private INavigatorActivationService navigatorActivationService;

     private NavigatorSaveablesService navigatorSaveablesService;

     private NavigatorExtensionStateService navigatorExtensionStateService;

     private IDescriptionProvider descriptionProvider;

     private boolean contentProviderInitialized;

     private boolean labelProviderInitialized;

     /**
      * @param aViewerId
      * The viewer id for this content service; normally from the
      * <b>org.eclipse.ui.views</b> extension.
      */
     public NavigatorContentService(String aViewerId) {
         super();
         aViewerId = aViewerId != null ? aViewerId : ""; //$NON-NLS-1$
 viewerDescriptor = VIEWER_DESCRIPTOR_REGISTRY
                 .getNavigatorViewerDescriptor(aViewerId);
         assistant = new VisibilityAssistant(viewerDescriptor,
                 getActivationService());
         getActivationService().addExtensionActivationListener(this);
     }

     /**
      * @param aViewerId
      * The viewer id for this content service; normally from the
      * <b>org.eclipse.ui.views</b> extension.
      * @param aViewer
      * The viewer that this content service will be associated with.
      */
     public NavigatorContentService(String aViewerId, StructuredViewer aViewer) {
         this(aViewerId);
         structuredViewerManager = new StructuredViewerManager(aViewer);
     }

     public String [] getVisibleExtensionIds() {

         List visibleExtensionIds = new ArrayList ();

         NavigatorContentDescriptor[] descriptors = CONTENT_DESCRIPTOR_REGISTRY
                 .getAllContentDescriptors();
         for (int i = 0; i < descriptors.length; i++) {
             if (assistant.isVisible(descriptors[i].getId())) {
                 visibleExtensionIds.add(descriptors[i].getId());
             }
         }
         if (visibleExtensionIds.isEmpty()) {
             return NO_EXTENSION_IDS;
         }
         return (String []) visibleExtensionIds
                 .toArray(new String [visibleExtensionIds.size()]);

     }

     public INavigatorContentDescriptor[] getVisibleExtensions() {
         List visibleDescriptors = new ArrayList ();

         NavigatorContentDescriptor[] descriptors = CONTENT_DESCRIPTOR_REGISTRY
                 .getAllContentDescriptors();
         for (int i = 0; i < descriptors.length; i++) {
             if (assistant.isVisible(descriptors[i].getId())) {
                 visibleDescriptors.add(descriptors[i]);
             }
         }
         if (visibleDescriptors.isEmpty()) {
             return NO_DESCRIPTORS;
         }
         return (INavigatorContentDescriptor[]) visibleDescriptors
                 .toArray(new INavigatorContentDescriptor[visibleDescriptors
                         .size()]);

     }

     /* package */INavigatorContentDescriptor[] getActiveDescriptorsWithSaveables() {
         List result = new ArrayList ();

         NavigatorContentDescriptor[] descriptors = CONTENT_DESCRIPTOR_REGISTRY
                 .getContentDescriptorsWithSaveables();
         for (int i = 0; i < descriptors.length; i++) {
             if (assistant.isVisible(descriptors[i].getId())
                     && assistant.isActive(descriptors[i])) {
                 result.add(descriptors[i]);
             }
         }
         if (result.isEmpty()) {
             return NO_DESCRIPTORS;
         }
         return (INavigatorContentDescriptor[]) result
                 .toArray(new INavigatorContentDescriptor[result.size()]);

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.navigator.INavigatorContentService#bindExtensions(java.lang.String[],
      * boolean)
      */
     public INavigatorContentDescriptor[] bindExtensions(String [] extensionIds,
             boolean isRoot) {
         if (extensionIds == null || extensionIds.length == 0) {
             return NO_DESCRIPTORS;
         }

         for (int i = 0; i < extensionIds.length; i++) {
             assistant.bindExtensions(extensionIds, isRoot);
         }
         Set boundDescriptors = new HashSet ();
         INavigatorContentDescriptor descriptor;
         for (int i = 0; i < extensionIds.length; i++) {
             descriptor = CONTENT_DESCRIPTOR_REGISTRY
                     .getContentDescriptor(extensionIds[i]);
             if (descriptor != null) {
                 boundDescriptors.add(descriptor);
             }
         }

         if (boundDescriptors.size() == 0) {
             return NO_DESCRIPTORS;
         }
         return (INavigatorContentDescriptor[]) boundDescriptors
                 .toArray(new INavigatorContentDescriptor[boundDescriptors
                         .size()]);

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#createCommonContentProvider()
      */
     public ITreeContentProvider createCommonContentProvider() {
         if (contentProviderInitialized) {
             return contentProvider;
         }
         synchronized (this) {
             if (contentProvider == null) {
                 contentProvider = new NavigatorContentServiceContentProvider(
                         this);
             }
             contentProviderInitialized = true;
         }
         return contentProvider;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#createCommonLabelProvider()
      */
     public ILabelProvider createCommonLabelProvider() {
         if (labelProviderInitialized) {
             return labelProvider;
         }
         synchronized (this) {
             if (labelProvider == null) {
                 labelProvider = new NavigatorContentServiceLabelProvider(this);
             }
             labelProviderInitialized = true;
         }
         return labelProvider;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.navigator.INavigatorContentService#createCommonDescriptionProvider()
      */
     public IDescriptionProvider createCommonDescriptionProvider() {
         if (descriptionProvider != null) {
             return descriptionProvider;
         }
         synchronized (this) {
             if (descriptionProvider == null) {
                 descriptionProvider = new NavigatorContentServiceDescriptionProvider(
                         this);
             }
         }
         return descriptionProvider;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#dispose()
      */
     public void dispose() {
         if (navigatorSaveablesService != null) {
             assistant.removeListener(navigatorSaveablesService);
         }
         for (Iterator contentItr = contentExtensions.values().iterator(); contentItr
                 .hasNext();) {
             ((NavigatorContentExtension) contentItr.next()).dispose();
         }
         getActivationService().removeExtensionActivationListener(this);
         assistant.dispose();
     }

     protected void updateService(Viewer aViewer, Object anOldInput,
             Object aNewInput) {

         synchronized (this) {

             if (structuredViewerManager == null) {
                 structuredViewerManager = new StructuredViewerManager(aViewer);
                 structuredViewerManager.inputChanged(anOldInput, aNewInput);
             } else {
                 structuredViewerManager.inputChanged(aViewer, anOldInput,
                         aNewInput);
             }

             for (Iterator contentItr = contentExtensions.values().iterator(); contentItr
                     .hasNext();) {
                 structuredViewerManager
                         .initialize(((NavigatorContentExtension) contentItr
                                 .next()).getContentProvider());
             }

             rootContentProviders = extractContentProviders(findRootContentExtensions(aNewInput));
         }
     }

     public IExtensionStateModel findStateModel(String anExtensionId) {
         if (anExtensionId == null) {
             return null;
         }
         INavigatorContentDescriptor desc = CONTENT_DESCRIPTOR_REGISTRY
                 .getContentDescriptor(anExtensionId);
         if (desc == null) {
             return null;
         }
         INavigatorContentExtension ext = getExtension(desc);
         if (ext == null) {
             return null;
         }
         return ext.getStateModel();
     }

     /**
      * Return a set of content providers that could provide a parent for the
      * given element. These content extensions are determined by consulting the
      * <b>possibleChildren</b> expression in the <b>navigatorContent</b>
      * extension.
      *
      * <p>
      * Clients that wish to tap into the link with editor support must describe
      * all of their possible children in their <b>possibleChildren</b>
      * expressions.
      * </p>
      *
      * @param anElement
      * An element from the tree (generally from a setSelection()
      * method).
      * @return The set of content providers that may be able to provide a
      * parent.
      */
     public ITreeContentProvider[] findParentContentProviders(Object anElement) {
         return extractContentProviders(findContentExtensionsWithPossibleChild(anElement));
     }

     /**
      * <p>
      * Return all of the content providers that are relevant for the viewer. The
      * viewer is determined by the ID used to create the
      * INavigatorContentService ({@link #getViewerId() }). See
      * {@link #createCommonContentProvider() } for more information about how
      * content providers are located for the root of the viewer. The root
      * content providers are calculated once. If a new element is supplied, a
      * client must call {@link #update() } prior in order to reset the
      * calculated root providers.
      * </p>
      *
      * @param anElement
      * An element from the tree (generally the input of the viewer)
      * @return The set of content providers that can provide root elements for a
      * viewer.
      */
     public ITreeContentProvider[] findRootContentProviders(Object anElement) {
         if (rootContentProviders != null) {
             return rootContentProviders;
         }
         synchronized (this) {
             if (rootContentProviders == null) {
                 rootContentProviders = extractContentProviders(findRootContentExtensions(anElement));

             }
         }
         return rootContentProviders;
     }

     /**
      *
      * Return all of the label providers that are enabled for the given element.
      * A label provider is 'enabled' if its corresponding content provider
      * returned the element, or the element is described in the content
      * extension's <b>triggerPoints</b> expression.
      *
      * @param anElement
      * An element from the tree (any element contributed to the
      * tree).
      * @return The set of label providers that may be able to provide a valid
      * (non-null) label.
      */
     public ILabelProvider[] findRelevantLabelProviders(Object anElement) {
         return extractLabelProviders(findContentExtensionsWithPossibleChild(
                 anElement, false));
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>triggerPoints</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * either declared through a
      * <b>org.eclipse.ui.navigator.viewer/viewerContentBinding</b> to
      * be a root element or have a <b>triggerPoints</b> expression that
      * is <i>enabled</i> for the given element.
      */
     public Set findRootContentExtensions(Object anElement) {
         return findRootContentExtensions(anElement, true);
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>triggerPoints</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @param toRespectViewerRoots
      * True respect the <b>viewerContentBinding</b>s, False will
      * look only for matching <b>triggerPoints</b> expressions.
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * either declared through a
      * <b>org.eclipse.ui.navigator.viewer/viewerContentBinding</b> to
      * be a root element or have a <b>triggerPoints</b> expression that
      * is <i>enabled</i> for the given element.
      */
     public Set findRootContentExtensions(Object anElement,
             boolean toRespectViewerRoots) {

         SortedSet rootExtensions = new TreeSet (
                 ExtensionPriorityComparator.INSTANCE);
         if (toRespectViewerRoots
                 && viewerDescriptor.hasOverriddenRootExtensions()) {

             NavigatorContentDescriptor[] descriptors = CONTENT_DESCRIPTOR_REGISTRY
                     .getAllContentDescriptors();

             NavigatorContentExtension extension = null;
             for (int i = 0; i < descriptors.length; i++) {
                 if (isActive(descriptors[i].getId())
                         && isRootExtension(descriptors[i].getId())) {
                     extension = getExtension(descriptors[i]);
                     if (!extension.hasLoadingFailed()) {
                         rootExtensions.add(extension);
                     }
                 }
             }
         }
         if (rootExtensions.isEmpty()) {
             return findContentExtensionsByTriggerPoint(anElement);
         }
         return rootExtensions;
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findOverrideableContentExtensionsByTriggerPoint(Object anElement) {
         Set overrideableExtensions = new TreeSet (
                 ExtensionPriorityComparator.INSTANCE);
         Set descriptors = findDescriptorsByTriggerPoint(anElement);
         for (Iterator iter = descriptors.iterator(); iter.hasNext();) {
             INavigatorContentDescriptor descriptor = (INavigatorContentDescriptor) iter
                     .next();
             if (descriptor.hasOverridingExtensions()) {
                 overrideableExtensions.add(getExtension(descriptor));
             }
         }
         return overrideableExtensions;
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findOverrideableContentExtensionsForPossibleChild(
             Object anElement) {
         Set overrideableExtensions = new TreeSet (
                 ExtensionPriorityComparator.INSTANCE);
         Set descriptors = findDescriptorsWithPossibleChild(anElement, false);
         for (Iterator iter = descriptors.iterator(); iter.hasNext();) {
             INavigatorContentDescriptor descriptor = (INavigatorContentDescriptor) iter
                     .next();
             if (descriptor.hasOverridingExtensions()) {
                 overrideableExtensions.add(getExtension(descriptor));
             }
         }
         return overrideableExtensions;
     }

     /*
      * (Non-Javadoc)
      *
      * @see INavigatorContentService#getContentDescriptorById(String)
      */
     public INavigatorContentDescriptor getContentDescriptorById(
             String anExtensionId) {
         return CONTENT_DESCRIPTOR_REGISTRY.getContentDescriptor(anExtensionId);
     }

     /**
      *
      * @param anExtensionId
      * The id used to define the
      * <b>org.eclipse.ui.navigator.navigatorContent/navigatorContent</b>
      * extension.
      * @return An instance of the content extension for the given extension id.
      * May return <b>null</b> if the id is invalid.
      */
     public INavigatorContentExtension getContentExtensionById(
             String anExtensionId) {
         NavigatorContentDescriptor descriptor = CONTENT_DESCRIPTOR_REGISTRY
                 .getContentDescriptor(anExtensionId);
         if (descriptor != null)
             return getExtension(descriptor);
         return null;
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>triggerPoints</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>triggerPoints</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findContentExtensionsByTriggerPoint(Object anElement) {
         return findContentExtensionsByTriggerPoint(anElement, true);
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>triggerPoints</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @param toLoadIfNecessary
      * True will force the load of the extension, False will not
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>triggerPoints</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findContentExtensionsByTriggerPoint(Object anElement,
             boolean toLoadIfNecessary) {
         Set enabledDescriptors = findDescriptorsByTriggerPoint(anElement);
         return extractDescriptorInstances(enabledDescriptors, toLoadIfNecessary);
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findContentExtensionsWithPossibleChild(Object anElement) {
         return findContentExtensionsWithPossibleChild(anElement, true);
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @param toLoadIfNecessary
      * True will force the load of the extension, False will not
      * @return The set of {@link INavigatorContentExtension}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findContentExtensionsWithPossibleChild(Object anElement,
             boolean toLoadIfNecessary) {
         Set enabledDescriptors = findDescriptorsWithPossibleChild(anElement);
         return extractDescriptorInstances(enabledDescriptors, toLoadIfNecessary);
     }

     /**
      * Remember that the elements in the given array came from the given source
      *
      * @param source
      * The descriptor of the extension that contributed the set of
      * elements.
      * @param elements
      * An array of elements from the given source.
      */
     public synchronized void rememberContribution(
             NavigatorContentDescriptor source, Object [] elements) {

         if (source != null && elements != null) {
             for (int i = 0; i < elements.length; i++) {
                 getContributionMemory().put(elements[i], source);
             }
         }
     }

     /**
      * Remember that the elements in the given array came from the given source
      *
      * @param source
      * The descriptor of the extension that contributed the set of
      * elements.
      * @param element
      * An element from the given source.
      */
     public synchronized void rememberContribution(
             NavigatorContentDescriptor source, Object element) {
         if (source != null && element != null) {
             getContributionMemory().put(element, source);
         }
     }

     /**
      *
      * @param element
      * The element contributed by the descriptor to be returned
      * @return The descriptor that contributed the element or null.
      * @see #findContentExtensionsWithPossibleChild(Object)
      */
     public NavigatorContentDescriptor getSourceOfContribution(Object element) {
         return (NavigatorContentDescriptor) getContributionMemory()
                 .get(element);
     }

     /**
      * @return Returns the contributionMemory.
      */
     public Map getContributionMemory() {
         if (contributionMemory != null) {
             return contributionMemory;
         }
         synchronized (this) {
             if (contributionMemory == null) {
                 contributionMemory = new WeakHashMap ();
             }
         }

         return contributionMemory;
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>triggerPoints</b> expression.
      *
      * @param anElement
      * The element to use in the query
      *
      * @return The set of {@link INavigatorContentDescriptor}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>triggerPoints</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findDescriptorsByTriggerPoint(Object anElement) {

         NavigatorContentDescriptor descriptor = getSourceOfContribution(anElement);
         Set result = new TreeSet (ExtensionPriorityComparator.INSTANCE);
         if (descriptor != null) {
             result.add(descriptor);
         }
         result.addAll(CONTENT_DESCRIPTOR_REGISTRY
                 .findDescriptorsForTriggerPoint(anElement, assistant));
         return result;
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @return The set of {@link INavigatorContentDescriptor}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findDescriptorsWithPossibleChild(Object anElement) {
         return findDescriptorsWithPossibleChild(anElement, true);
     }

     /**
      * Search for extensions that declare the given element in their
      * <b>possibleChildren</b> expression.
      *
      * @param anElement
      * The element to use in the query
      * @param toComputeOverrides
      * True indicates the overridden tree should be traversed.
      * @return The set of {@link INavigatorContentDescriptor}s that are
      * <i>visible</i> and <i>active</i> for this content service and
      * have a <b>possibleChildren</b> expression that is <i>enabled</i>
      * for the given element.
      */
     public Set findDescriptorsWithPossibleChild(Object anElement,
             boolean toComputeOverrides) {

         NavigatorContentDescriptor descriptor = getSourceOfContribution(anElement);
         Set result = new TreeSet (ExtensionPriorityComparator.INSTANCE);
         if (descriptor != null) {
             result.add(descriptor);
         }
         result.addAll(CONTENT_DESCRIPTOR_REGISTRY
                 .findDescriptorsForPossibleChild(anElement, assistant,
                         toComputeOverrides));
         return result;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#onExtensionActivation(java.lang.String,
      * java.lang.String, boolean)
      */
     public void onExtensionActivation(String aViewerId,
             String [] aNavigatorExtensionId, boolean toEnable) {
         synchronized (this) {
             try {
                 NavigatorContentDescriptor key;
                 NavigatorContentExtension extension;
                 for (Iterator iter = contentExtensions.keySet().iterator(); iter
                         .hasNext();) {
                     key = (NavigatorContentDescriptor) iter.next();
                     INavigatorActivationService activation = getActivationService();
                     if (!activation.isNavigatorExtensionActive(key.getId())) {
                         extension = (NavigatorContentExtension) contentExtensions
                                 .get(key);
                         iter.remove();
                         /*
                          * There really shouldn't be any way that this can be
                          * null, but just to be safe
                          */
                         if (extension != null) {
                             extension.dispose();
                         }
                     }
                 }
             } catch (RuntimeException e) {
                 String msg = e.getMessage() != null ? e.getMessage() : e
                         .toString();
                 NavigatorPlugin.logError(0, msg, e);
             }
         }
         update();
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#update()
      */
     public void update() {
         rootContentProviders = null;
         if (structuredViewerManager != null) {
             structuredViewerManager.safeRefresh();
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#getViewerId()
      */
     public final String getViewerId() {
         return viewerDescriptor.getViewerId();
     }

     /**
      *
      * @param aDescriptorKey
      * A descriptor
      * @return The cached NavigatorContentExtension from the descriptor
      */
     public final NavigatorContentExtension getExtension(
             INavigatorContentDescriptor aDescriptorKey) {
         return getExtension(aDescriptorKey, true);
     }

     /**
      *
      * @param aDescriptorKey
      * @param toLoadIfNecessary
      * True if the extension should be loaded if it is not already.
      * @return The instance of the extension for the given descriptor key.
      */
     public final NavigatorContentExtension getExtension(
             INavigatorContentDescriptor aDescriptorKey,
             boolean toLoadIfNecessary) {
         /* Query and return the relevant descriptor instance */
         NavigatorContentExtension extension = (NavigatorContentExtension) contentExtensions
                 .get(aDescriptorKey);
         if (extension != null || !toLoadIfNecessary) {
             return extension;
         }

         /*
          * If the descriptor instance hasn't been created yet, then we need to
          * (1) verify that it wasn't added by another thread, (2) create and add
          * the result into the map
          */
         synchronized (this) {
             extension = (NavigatorContentExtension) contentExtensions
                     .get(aDescriptorKey);
             if (extension == null) {
                 contentExtensions.put(aDescriptorKey,
                         (extension = new NavigatorContentExtension(
                                 (NavigatorContentDescriptor) aDescriptorKey,
                                 this, structuredViewerManager)));
                 notifyListeners(extension);
             }
         }
         return extension;

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#getViewerDescriptor()
      */
     public INavigatorViewerDescriptor getViewerDescriptor() {
         return viewerDescriptor;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#restoreState(org.eclipse.ui.IMemento)
      */
     public void restoreState(final IMemento aMemento) {
         synchronized (this) {
             List runnables = new ArrayList ();
             for (Iterator extensionItr = getExtensions().iterator(); extensionItr
                     .hasNext();) {
                 final NavigatorContentExtension element = (NavigatorContentExtension) extensionItr
                         .next();
                 ISafeRunnable runnable = new ISafeRunnable() {
                     public void run() throws Exception {
                         element.restoreState(aMemento);
                     }

                     public void handleException(Throwable exception) {
                         NavigatorPlugin.logError(0,
                                 "Could not restore state for Common Navigator content extension" //$NON-NLS-1$
 + element.getId(), exception);
                     }
                 };
                 runnables.add(runnable);
             }
             for (Iterator iterator = runnables.iterator(); iterator.hasNext();) {
                 ISafeRunnable runnable = (ISafeRunnable) iterator.next();
                 SafeRunner.run(runnable);
             }
         }
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#saveState(org.eclipse.ui.IMemento)
      */
     public void saveState(final IMemento aMemento) {
         synchronized (this) {
             
             List runnables = new ArrayList ();
             for (Iterator extensionItr = getExtensions().iterator(); extensionItr
                     .hasNext();) {
                 final NavigatorContentExtension element = (NavigatorContentExtension) extensionItr
                         .next();
                 ISafeRunnable runnable = new ISafeRunnable() {
                     public void run() throws Exception {
                         element.saveState(aMemento);
                     }

                     public void handleException(Throwable exception) {
                         NavigatorPlugin.logError(0,
                                 "Could not save state for Common Navigator content extension" //$NON-NLS-1$
 + element.getId(), exception);
                     }
                 };
                 runnables.add(runnable);
             }
             for (Iterator iterator = runnables.iterator(); iterator.hasNext();) {
                 ISafeRunnable runnable = (ISafeRunnable) iterator.next();
                 SafeRunner.run(runnable);
             }
         }
     }

     public boolean isActive(String anExtensionId) {
         return assistant.isActive(anExtensionId);
     }

     public boolean isVisible(String anExtensionId) {
         return assistant.isVisible(anExtensionId);
     }

     protected final Collection getExtensions() {
         return (contentExtensions.size() > 0) ? Collections
                 .unmodifiableCollection(contentExtensions.values())
                 : Collections.EMPTY_LIST;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.internal.navigator.INavigatorContentService#addListener(org.eclipse.ui.internal.navigator.extensions.INavigatorContentServiceListener)
      */
     public void addListener(INavigatorContentServiceListener aListener) {
         listeners.add(aListener);
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.navigator.INavigatorContentService#getFilterService()
      */
     public INavigatorFilterService getFilterService() {
         if (navigatorFilterService == null) {
             navigatorFilterService = new NavigatorFilterService(this);
         }
         return navigatorFilterService;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.navigator.INavigatorContentService#getFilterService()
      */
     public INavigatorSorterService getSorterService() {
         if (navigatorSorterService == null) {
             navigatorSorterService = new NavigatorSorterService(this);
         }
         return navigatorSorterService;
     }

     /*
      * (non-Javadoc)
      *
     * @see org.eclipse.ui.navigator.INavigatorContentService#getFilterService()
     */
    public INavigatorPipelineService getPipelineService() {
        if (navigatorPipelineService == null) {
            navigatorPipelineService = new NavigatorPipelineService(this);
        }
        return navigatorPipelineService;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.navigator.INavigatorContentService#getDnDService()
     */
    public INavigatorDnDService getDnDService() {
        if (navigatorDnDService == null) {
            navigatorDnDService = new NavigatorDnDService(this);
        }
        return navigatorDnDService;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.navigator.INavigatorContentService#getActivationService()
     */
    public INavigatorActivationService getActivationService() {

        if (navigatorActivationService == null) {
            navigatorActivationService = new NavigatorActivationService(this);
        }
        return navigatorActivationService;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.navigator.INavigatorContentService#getSaveableService()
     */
    public INavigatorSaveablesService getSaveablesService() {
        synchronized (this) {
            if (navigatorSaveablesService == null) {
                navigatorSaveablesService = new NavigatorSaveablesService(this);
                assistant.addListener(navigatorSaveablesService);
            }
            return navigatorSaveablesService;
        }
    }

    /**
     * Not API as of 3.3.
     *
     * @return The extension state service for this content service.
     *
     */
    public NavigatorExtensionStateService getExtensionStateService() {
        if (navigatorExtensionStateService == null) {
            navigatorExtensionStateService = new NavigatorExtensionStateService(
                    this);
        }
        return navigatorExtensionStateService;
    }

    /**
     * Non-API method to return a shell.
     *
     * @return A shell associated with the current viewer (if any) or <b>null</b>.
     */
    public Shell getShell() {
        if (structuredViewerManager != null
                && structuredViewerManager.getViewer() != null) {
            return structuredViewerManager.getViewer().getControl().getShell();
        }
        return null;
    }

    protected boolean isRootExtension(String anExtensionId) {
        return assistant.isRootExtension(anExtensionId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ui.internal.navigator.INavigatorContentService#removeListener(org.eclipse.ui.internal.navigator.extensions.INavigatorContentServiceListener)
     */
    public void removeListener(INavigatorContentServiceListener aListener) {
        listeners.remove(aListener);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "ContentService[" + viewerDescriptor.getViewerId() + "]"; //$NON-NLS-1$//$NON-NLS-2$
 }

    private void notifyListeners(NavigatorContentExtension aDescriptorInstance) {

        if (listeners.size() == 0) {
            return;
        }
        INavigatorContentServiceListener listener = null;
        List failedListeners = null;
        for (Iterator listenersItr = listeners.iterator(); listenersItr
                .hasNext();) {
            try {
                listener = (INavigatorContentServiceListener) listenersItr
                        .next();
                listener.onLoad(aDescriptorInstance);
            } catch (RuntimeException re) {
                if (failedListeners == null) {
                    failedListeners = new ArrayList ();
                }
                failedListeners.add(listener);
            }
        }
        if (failedListeners != null) {
            listeners.removeAll(failedListeners);
        }
    }

    private ITreeContentProvider[] extractContentProviders(
            Set theDescriptorInstances) {
        if (theDescriptorInstances.size() == 0) {
            return NO_CONTENT_PROVIDERS;
        }
        List resultProvidersList = new ArrayList ();
        for (Iterator itr = theDescriptorInstances.iterator(); itr.hasNext();) {
            resultProvidersList.add(((NavigatorContentExtension) itr.next())
                    .internalGetContentProvider());
        }
        return (ITreeContentProvider[]) resultProvidersList
                .toArray(new ITreeContentProvider[resultProvidersList.size()]);
    }

    private Set extractDescriptorInstances(Set theDescriptors,
            boolean toLoadAllIfNecessary) {
        if (theDescriptors.size() == 0) {
            return Collections.EMPTY_SET;
        }
        Set resultInstances = new TreeSet (ExtensionPriorityComparator.INSTANCE);
        for (Iterator descriptorIter = theDescriptors.iterator(); descriptorIter
                .hasNext();) {
            NavigatorContentExtension extension = getExtension(
                    (NavigatorContentDescriptor) descriptorIter.next(),
                    toLoadAllIfNecessary);
            if (extension != null) {
                resultInstances.add(extension);

            }
        }
        return resultInstances;
    }

    private ILabelProvider[] extractLabelProviders(Set theDescriptorInstances) {
        if (theDescriptorInstances.size() == 0) {
            return NO_LABEL_PROVIDERS;
        }
        List resultProvidersList = new ArrayList ();
        for (Iterator itr = theDescriptorInstances.iterator(); itr.hasNext();) {
            resultProvidersList.add(((NavigatorContentExtension) itr.next())
                    .getLabelProvider());
        }
        return (ILabelProvider[]) resultProvidersList
                .toArray(new ILabelProvider[resultProvidersList.size()]);
    }

}

